传送门:
请求过程
在Axios.prototype.request
中我们看到,要先通过请求拦截器,才能进行请求。下面看一下dispatchRequest()
是如何实现的
// /lib/core/dispatchRequest.js
module.exports = function dispatchRequest(config) {
// 判断是否已经取消请求
throwIfCancellationRequested(config);
/* 对请求的url、headers、data进行处理 */
// 发动请求的函数,返回一个promise
var adapter = config.adapter || defaults.adapter;
return adapter(config).then(function onAdapterResolution(response) {
// 判断是否已经取消请求
throwIfCancellationRequested(config);
// 处理返回的数据
response.data = transformData(
response.data,
response.headers,
config.transformResponse
);
return response;
}, function onAdapterRejection(reason) {
if (!isCancel(reason)) {
// 判断是否已经取消请求
throwIfCancellationRequested(config);
// 处理返回的错误信息
if (reason && reason.response) {
reason.response.data = transformData(
reason.response.data,
reason.response.headers,
config.transformResponse
);
}
}
return Promise.reject(reason);
});
如果用户有在配置中传入adapter
,将使用defaults.adapter
,根据运行环境是浏览器还是nodejs采取不同的请求方式。
// /lib/defaults.js
function getDefaultAdapter() {
var adapter;
if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
// nodejs环境
adapter = require('./adapters/http');
} else if (typeof XMLHttpRequest !== 'undefined') {
// 浏览器环境
adapter = require('./adapters/xhr');
}
return adapter;
}
var defaults = {
adapter: getDefaultAdapter(),
/* 其他配置 */
};
modules.exports = defaults;
/lib/adapters/http.js
与/lib/adapters/xhr.js
两个文件导出的函数都返回一个promise,具体的实现方式就不分析了。里面有很多http请求的细节,可以仔细研究。
取消请求
官方文档中的调用方法
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('/user/12345', {
cancelToken: source.token
}).catch(function(thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// handle error
}
});
axios.post('/user/12345', {
name: 'new name'
}, {
cancelToken: source.token
})
// cancel the request (the message parameter is optional)
source.cancel('Operation canceled by the user.');
我们进入CancelToken
类,找到了CancelToken.source()
方法:
// /lib/cancel/CancelToken
CancelToken.source = function source() {
var cancel;
var token = new CancelToken(function executor(c) {
cancel = c;
});
return {
token: token,
cancel: cancel
};
};
可以看出,CancelToken.source().token
是一个CancelToken
类的实例,CancelToken.source().cancel
是new CacelToken()
时传入参数(一个函数)的参数(也是个函数),通过CancelToken
的构造函数可以看出:
// /lib/cancel/CancelToken
function CancelToken(executor) {
if (typeof executor !== 'function') {
throw new TypeError('executor must be a function.');
}
var resolvePromise;
this.promise = new Promise(function promiseExecutor(resolve) {
resolvePromise = resolve;
});
var token = this;
executor(function cancel(message) {
if (token.reason) {
// Cancellation has already been requested
return;
}
token.reason = new Cancel(message);
resolvePromise(token.reason);
});
}
CancelToken.source().cancel
就是这个函数:
function cancel(message) {
if (token.reason) {
// Cancellation has already been requested
return;
}
token.reason = new Cancel(message);
resolvePromise(token.reason);
}
CancelToken.source().token
有promise
和reason
两个属性,promise
一直处于 pending
状态,reason
属性是一个Cancel
类的实例,Cancel
类的构造函数如下:
// /lib/cancel/Cancel.js
function Cancel(message) {
this.message = message;
}
Cancel.prototype.toString = function toString() {
return 'Cancel' + (this.message ? ': ' + this.message : '');
};
Cancel.prototype.__CANCEL__ = true;
在源码中,有以下几种方式检测是否执行了取消请求。
1 检测config.cancelToken
是否有reason
属性,如果有,将reason
抛出,axios
进入rejected
状态。
// /lib/core/dispatchRequest.js
function throwIfCancellationRequested(config) {
if (config.cancelToken) {
config.cancelToken.throwIfRequested();
}
}
module.exports = function dispatchRequest(config) {
// 判断是否已经取消请求
throwIfCancellationRequested(config);
/* ... */
};
// /lib/cancel/CancelToken
CancelToken.prototype.throwIfRequested = function throwIfRequested() {
if (this.reason) {
throw this.reason;
}
};
2 在请求过程中,执行CancelToken.source().token
的promise
属性中的resolve
函数,参数是CancelToken.source().token.reason
,并将其抛出,promise进入rejected
状态
if (config.cancelToken) {
// Handle cancellation
config.cancelToken.promise.then(function onCanceled(cancel) {
if (!request) {
return;
}
// 取消请求
request.abort();
// promise进入rejected
reject(cancel);
// Clean up request
request = null;
});
}
调用方法中catch
接到的thrown
,就是CancelToken.source().token.reason
。
如果在使用axios时候,只在config
中添加{cancelToken: source.token}
,而不调用source.cancel()
,则CancelToken.source().token
不会有reason
属性,CancelToken.source().token.promise
也一直是pending
状态。请求不会取消。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。